// $URL: http://mingw-lib.googlecode.com/svn/trunk/c-drive/mingw-lib/library/t_yt_dump_lib/t_youtube_dump_lib.cpp $
// $Rev: 862 $
// $Author: akio.miyoshi $
// $Date:: 2013-08-08 12:05:41 +0000#$

#include "t_youtube_dump_lib.h"
#include <QtGui>

T_Youtube_Dump_Lib::T_Youtube_Dump_Lib()
{
}

QSqlDatabase T_Youtube_Dump_Lib::getDumpDatabase()
{
#if 0x0
    ////QString v_db_path = "E:/Qt/yt_dump.mdb";
    QString v_db_path = "C:/yt_dump.mdb";
    QSqlDatabase m_db = QSqlDatabase::addDatabase("QODBC");
    m_db.setDatabaseName(
            QString("DRIVER={Microsoft Access Driver (*.mdb)};FIL={MS Access};DBQ=%1;")
            .arg(v_db_path)
            );
#else
    QString v_db_path = "C:/Qt/yt_dump.sqlite";
    QSqlDatabase m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setDatabaseName(v_db_path);
#endif
    return m_db;
}

void T_Youtube_Dump_Lib::getDownloadExe(QFile &a_file)
{
    //a_file.setFileName("E:/qt/mingw-lib/yt_download_top-build-desktop-Qt_4_8_1_for_Desktop_-_MinGW__Qt_SDK__Release/box/yt_download/release/yt_download.exe");
    //a_file.setFileName("C:/usr/local/debug/yt_download.exe");
    a_file.setFileName("C:/usr/local/release/yt_download.exe");
}

static QString slice(QString s, int a1)
{
    return s.mid(a1, 1);
}

static QString slice(QString s, int a1, int a2)
{
    Q_ASSERT(a1 < a2);
    return s.mid(a1, a2-a1);
}

static QString slice(QString s, int a1, int a2, int a3)
{
    Q_ASSERT(a3==-1);
    Q_ASSERT(a1 > a2);
    QString v_result;
    for(int i=a1; i>a2; i--)
    {
        v_result.append(s.mid(i, 1));
    }
    return v_result;
}

static QString decrypt_signature(QString s)
{
    qDebug() << "decrypt_signature()" << s.size();
    // 2013.07.25.2
    if (s.size() == 92)
        return slice(s,25) + slice(s,3,25) + slice(s,0) + slice(s,26,42) + slice(s,79)
                + slice(s,43,79) + slice(s,91) + slice(s,80,83);
    else if (s.size() == 90)
        return slice(s,25) + slice(s,3,25) + slice(s,2) + slice(s,26,40) + slice(s,77)
                + slice(s,41,77) + slice(s,89) + slice(s,78,81);
    else if (s.size() == 88)
        return slice(s,48) + slice(s,81,67,-1) + slice(s,82) + slice(s,66,62,-1) + slice(s,85)
                + slice(s,61,48,-1) + slice(s,67) + slice(s,47,12,-1) + slice(s,3) + slice(s,11,3,-1)
                + slice(s,2) + slice(s,12);
    else if (s.size() == 87)
        return slice(s,4,23) + slice(s,86) + slice(s,24,85);
    else if (s.size() == 86)
        return slice(s,2,63) + slice(s,82) + slice(s,64,82) + slice(s,63);
    else if (s.size() == 85)
        return slice(s,2,8) + slice(s,0) + slice(s,9,21) + slice(s,65) + slice(s,22,65)
                + slice(s,84) + slice(s,66,82) + slice(s,21);
    else if (s.size() == 84)
        return slice(s,83,36,-1) + slice(s,2) + slice(s,35,26,-1) + slice(s,3) + slice(s,25,3,-1)
                + slice(s,26);
    else if (s.size() == 83)
        return slice(s,6) + slice(s,3,6) + slice(s,33) + slice(s,7,24) + slice(s,0)
                + slice(s,25,33) + slice(s,53) + slice(s,34,53) + slice(s,24) + slice(s,54,83);
    else if (s.size() == 82)
        return slice(s,36) + slice(s,79,67,-1) + slice(s,81) + slice(s,66,40,-1) + slice(s,33)
                + slice(s,39,36,-1) + slice(s,40) + slice(s,35) + slice(s,0) + slice(s,67)
                + slice(s,32,0,-1) + slice(s,34);
    else if (s.size() == 81)
        return slice(s,56) + slice(s,79,56,-1) + slice(s,41) + slice(s,55,41,-1) + slice(s,80)
                + slice(s,40,34,-1) + slice(s,0) + slice(s,33,29,-1) + slice(s,34)
                + slice(s,28,9,-1) + slice(s,29) + slice(s,8,0,-1)
                     + slice(s,9);
    else if (s.size() == 79)
        return slice(s,54) + slice(s,77,54,-1) + slice(s,39) + slice(s,53,39,-1) + slice(s,78)
                + slice(s,38,34,-1) + slice(s,0) + slice(s,33,29,-1) + slice(s,34)
                + slice(s,28,9,-1) + slice(s,29) + slice(s,8,0,-1) + slice(s,9);
    else return "";
}

QString T_Youtube_Dump_Lib::getVideoStreamMap(
        QNetworkAccessManager *a_nam, QString a_id, QMap<int, QUrl> &a_fmt_map, QStringList &a_error_list)
{
    T_Youtube_Video_Info v_video_info;
    QString v_token = T_Youtube_Dump_Lib::getVideoInfo(
            a_nam,
            a_id,
            &v_video_info);
    a_fmt_map = v_video_info.f_fmt_map;
    a_error_list = v_video_info.f_error_list;
    return v_token;
}

QString T_Youtube_Dump_Lib::getVideoInfo(
        QNetworkAccessManager *a_nam, QString a_id, T_Youtube_Video_Info *a_video_info)
{
    Q_ASSERT(!!a_video_info);
    a_video_info->f_id = a_id;
    a_video_info->f_title = "";
    a_video_info->f_error_list.clear();
    a_video_info->f_fmt_map.clear();
    a_video_info->f_fmt_size_map.clear();
#if 0x0
    batchGet(a_nam, QString("www.youtube.com/watch?v=%1").arg(a_id));
#endif
    //
    QString v_token = "";
    //[Get Token]
#if 0x0
    QString v_url_1 =
            QString("http://www.youtube.com/get_video_info?&video_id=%1&el=detailpage&ps=default&eurl=&gl=US&hl=en")
            .arg(a_id);
#endif
//# We simulate the access to the video from www.youtube.com/v/{video_id}
//# this can be viewed without login into Youtube
//data = compat_urllib_parse.urlencode({'video_id': video_id,
//                                      'el': 'embedded',
//                                      'gl': 'US',
//                                      'hl': 'en',
//                                      'eurl': 'https://youtube.googleapis.com/v/' + video_id,
//                                      'asv': 3,
//                                      'sts':'1588',
//                                      })
//video_info_url = 'https://www.youtube.com/get_video_info?' + data
#if 0x0
    QString v_url_1 =
            QString("http://www.youtube.com/get_video_info?"
                    "video_id=%1"
                    "&el=embedded"
                    "&gl=US"
                    "&hl=en"
                    "&eurl=https://youtube.googleapis.com/v/%1"
                    "&asv=3"
                    "&sts=1588")
    .arg(a_id);
#else
    QUrl v_url_0("http://www.youtube.com/get_video_info?");
    v_url_0.addQueryItem("video_id", a_id);
    v_url_0.addQueryItem("el", "embedded");
    v_url_0.addQueryItem("gl", "US");
    v_url_0.addQueryItem("hl", "en");
    v_url_0.addQueryItem("eurl", QString("https://youtube.googleapis.com/v/%1").arg(a_id));
    v_url_0.addQueryItem("asv", "3");
    v_url_0.addQueryItem("sts", "1588");
    QString v_url_1 = v_url_0.toString();
#endif
    QUrl v_url_2(v_url_1);
    qDebug() << "[v_url_2.toEncoded()]" << v_url_2.toEncoded();

    QByteArray v_params_1 = batchGet(a_nam, v_url_1);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QString v_dirPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
#else
    v_params_1.prepend("?");
    QString v_dirPath = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation);
#endif
    QDir v_dir(v_dirPath);
    QString v_filePath = v_dir.filePath("getTokenForVideoStreamEmbed.txt");
    qDebug() << "[getTokenForVideoStreamEmbed()]" << v_filePath;
    QFile v_file(v_filePath);
    v_file.open(QIODevice::WriteOnly);
    v_file.write(v_params_1);
    v_file.close();

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QUrlQuery v_params_2(v_params_1);
#else
    QUrl v_params_2;
    v_params_2.setEncodedUrl(v_params_1);
#endif
    //[status]
    QString v_status = v_params_2.queryItemValue("status");
    qDebug() << "[v_status]" << v_status;
    //[reason]
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QString v_reason = v_params_2.queryItemValue("reason");
    v_reason.replace("+", " ");
    v_reason = QUrl::fromPercentEncoding(v_reason.toUtf8());
#else
    QByteArray v_reason_array = v_params_2.encodedQueryItemValue("reason");
    v_reason_array.replace("+", " ");
    QString v_reason = QUrl::fromPercentEncoding(v_reason_array);
#endif
    //[title]
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QString v_title = v_params_2.queryItemValue("title");
    v_title.replace("+", " ");
    a_video_info->f_title = QUrl::fromPercentEncoding(v_title.toUtf8());
#else
    QByteArray v_title_array = v_params_2.encodedQueryItemValue("title");
    v_title_array.replace("+", " ");
    a_video_info->f_title = QUrl::fromPercentEncoding(v_title_array);
#endif
    //[token]
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    v_token = v_params_2.queryItemValue("token");
#else
    v_token = v_params_2.encodedQueryItemValue("token");
#endif
    //[status == fail]
    if(v_status.isEmpty())
    {
        a_video_info->f_error_list.append("ネットワークから応答がありません");
        return "";
    }
    else if(v_status == "fail")
    {
        a_video_info->f_error_list.append(v_reason);
        return "";
    }

    QString v_use_cipher_signature = v_params_2.queryItemValue("use_cipher_signature");
    qDebug() << "[v_use_cipher_signature]" << v_use_cipher_signature;

    //[v_fmt_size_map]
    QString v_fmt_list = v_params_2.queryItemValue("fmt_list");
    qDebug() << "[v_fmt_list]" << v_fmt_list;
    ////QMap<int, QString> f_fmt_size_map;
    a_video_info->f_fmt_size_map.clear();
    QStringList v_fmt_size_split = v_fmt_list.split(",");
    for(int i=0; i<v_fmt_size_split.size(); i++)
    {
        QStringList v_split = v_fmt_size_split.at(i).split("/");
        a_video_info->f_fmt_size_map.insert(v_split.at(0).toInt(), v_split.at(1));
    }
    qDebug() << "[v_fmt_size_map]" << a_video_info->f_fmt_size_map;
    //[url_encoded_fmt_stream_map]
    QString v_fmt_stream_map = v_params_2.queryItemValue("url_encoded_fmt_stream_map");
    qDebug() << "[v_status(getTokenForVideoStreamEmbed)]" << v_status << v_reason;
    a_video_info->f_fmt_map.clear();
    if(v_fmt_stream_map != "")
    {
        QStringList v_slist_1 = v_fmt_stream_map.split(',');
        for(int i=0; i<v_slist_1.size(); i++)
        {
            QByteArray v_params_1 = v_slist_1.at(i).toLatin1();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
            ////v_params_1.prepend("?");
            QUrlQuery v_params_2(v_params_1);
#else
            v_params_1.prepend("?");
            QUrl v_params_2;
            v_params_2.setEncodedUrl(v_params_1);
#endif

            QString v_stream_url = v_params_2.queryItemValue("url");
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
            v_stream_url = QUrl::fromPercentEncoding(v_stream_url.toLatin1());
            v_stream_url = QUrl::fromPercentEncoding(v_stream_url.toLatin1());
            v_stream_url = QUrl::fromPercentEncoding(v_stream_url.toLatin1());
#endif
            if(!v_params_2.hasQueryItem("url"))
            {
                continue;
            }
            if(v_use_cipher_signature.toLower()=="true")
            {
                QString v_sig = v_params_2.queryItemValue("s");
                qDebug() << "[v_s](0)" << v_sig << v_sig.size();
                v_sig = decrypt_signature(v_sig);
                qDebug() << "[v_s](1)" << v_sig << v_sig.size();
                v_stream_url += "&signature=" + v_sig;
                ////v_stream_url += "&ratebypass=yes";
                qDebug() << "[v_stream_url]" << v_stream_url;
            }
            else
            {
                QString v_sig = v_params_2.queryItemValue("sig");
                qDebug() << "[v_sig(1)]" << v_sig;
                v_stream_url += "&signature=" + v_sig;
                qDebug() << "[v_stream_url]" << v_stream_url;
            }
            int v_fmt = v_params_2.queryItemValue("itag").toInt();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
            QString v_decoded_url = v_stream_url;
#else
            QString v_decoded_url = QUrl::fromPercentEncoding(v_stream_url.toLatin1());
#endif
            QUrl v_stream_url_2(v_decoded_url);
            a_video_info->f_fmt_map.insert(v_fmt, v_stream_url_2);
        }
    }
    if(a_video_info->f_fmt_map.isEmpty())
    {
        a_video_info->f_error_list.append("ダウンロード可能なURLが見つかりません");
        return "";
    }
    qDebug() << "[v_token]" << v_token;
    return v_token;
}

QString T_Youtube_Dump_Lib::getFFMpegExe()
{
    QString v_app_dir_path = QCoreApplication::applicationDirPath();
    qDebug() << "[T_Youtube_Dump_Lib::getFFMpegExe(1a)]" << v_app_dir_path;
    QString v_dl_exe_path = v_app_dir_path + "/../../tools/ffmpeg/bin/ffmpeg.exe";
    qDebug() << "[T_Youtube_Dump_Lib::getFFMpegExe(2e)]" << v_dl_exe_path;
    if(QFileInfo(v_dl_exe_path).exists())
    {
        QString v_cann_file_path = QFileInfo(v_dl_exe_path).canonicalFilePath();
        v_cann_file_path = QDir::toNativeSeparators(v_cann_file_path);
        return v_cann_file_path;
    }
    return "ffmpeg.exe";
}

QString T_Youtube_Dump_Lib::getToolsExe(const QString &a_exe_name, bool a_quote)
{
    QString v_app_dir_path = QCoreApplication::applicationDirPath();
    qDebug() << "[T_Youtube_Dump_Lib::getToolsExe(1a)]" << v_app_dir_path;
    QString v_dl_exe_path = v_app_dir_path + "/../../tools/"+ a_exe_name + "/bin/" + a_exe_name + ".exe";
    qDebug() << "[T_Youtube_Dump_Lib::getToolsExe(2e)]" << v_dl_exe_path;
    if(QFileInfo(v_dl_exe_path).exists())
    {
        QString v_cann_file_path = QFileInfo(v_dl_exe_path).canonicalFilePath();
        v_cann_file_path = QDir::toNativeSeparators(v_cann_file_path);
        if(a_quote)
        {
            v_cann_file_path = QString("\"") + v_cann_file_path + QString("\"");
        }
        return v_cann_file_path;
    }
    if(a_quote)
    {
        return QString("\"") + a_exe_name + QString("\"");
    }
    return a_exe_name;
}
